﻿using GeneticAlgorithm.Interfaces;
using System;
using System.Collections.Generic;

namespace GeneticAlgorithm.Models
{
    public class ImageGenerateGeneticAlgorithm : IGeneticAlgorithm<int>
    {
        public delegate void OutMethod(string a);

        public OutMethod Out { get; set; }

        public StopCondition Condition { get; set; } = StopCondition.ReachPresupposedCondition;

        public int IterationNumber { get; set; }

        /// <summary>
        /// 初始种群个体数量
        /// </summary>
        public int InitialPopulationNumber { get; set; } = 6;

        /// <summary>
        /// 每次繁殖产生基因突变的数量
        /// </summary>
        public int GeneMutationNumber { get; set; } = 3;

        public List<IEvolvable<int>> Population { get; set; } = new List<IEvolvable<int>>();

        public int CurrentIterationNumber { get; set; }

        public IEvolvable<int> BestIndividual
        {
            get
            {
                Sort(Population);
                return Population[Population.Count - 1];
            }
        }

        public List<IEvolvable<int>> GenerateInitialPopulation()
        {
            List<IEvolvable<int>> temp = new List<IEvolvable<int>>(InitialPopulationNumber);
            for (int i = 0; i < InitialPopulationNumber; i++)
                temp.Add(new Matrix());
            return temp;
        }

        public bool ReachPresupposedCondition()
        {
            foreach (var item in Population)
            {
                if (item.Fitness == 0)
                    return true;
            }
            return false;
        }

        public List<IEvolvable<int>> Reproduce(List<IEvolvable<int>> parents)
        {
            List<IEvolvable<int>> children = new List<IEvolvable<int>>();
            for (int i = 0; i < Population.Count - 1; i++)
                children.Add(ExchangeGenes(Population[i] as Matrix, Population[i + 1] as Matrix));
            return children;
        }

        public List<IEvolvable<int>> ScreenIndividuals(List<IEvolvable<int>> originalPopulation)
        {
            Sort(originalPopulation);
            originalPopulation.RemoveRange(0, originalPopulation.Count - InitialPopulationNumber);
            return originalPopulation;
        }

        public List<IEvolvable<int>> ScreenParents(List<IEvolvable<int>> originalPopulation)
        {
            return originalPopulation;
        }

        /// <summary>
        /// 基因交换与变异
        /// </summary>
        /// <param name="a">亲代a</param>
        /// <param name="b">亲代b</param>
        /// <returns>子代</returns>
        private IEvolvable<int> ExchangeGenes(Matrix a, Matrix b)
        {
            List<Gene<int>> temp = new List<Gene<int>>(Matrix.MatrixLength);
            for (int i = 0; i < Matrix.MatrixLength; i++)
            {
                if (i % 2 == 0)
                    temp.AddRange((a as IEvolvable<int>).GetGeneSequenceRange(i, 1));
                else
                    temp.AddRange((b as IEvolvable<int>).GetGeneSequenceRange(i, 1));
            }
            var Position = Matrix.GetRandomArray(GeneMutationNumber, 0, 114514 % a.GeneSequence.Count);
            for (int i = 0; i < GeneMutationNumber; i++)
                temp[Position[i]].GeneInformation = temp[Position[i]].GeneInformation == 0 ? 1 : 0;
            return new Matrix(temp);
        }

        /// <summary>
        /// 按照适应度从大到小排列
        /// </summary>
        /// <param name="originalPopulation">原始种群</param>
        public void Sort(List<IEvolvable<int>> originalPopulation)
        {
            for (int j = 0; j < originalPopulation.Count; j++)
            {
                for (int i = 0; i < originalPopulation.Count - 1; i++)
                {
                    if (originalPopulation[i].Fitness < originalPopulation[i + 1].Fitness)
                    {
                        var temp = originalPopulation[i];
                        originalPopulation[i] = originalPopulation[i + 1];
                        originalPopulation[i + 1] = temp;
                    }
                }
            }
        }

        public void Execute()
        {
            Execute(GenerateInitialPopulation());
        }

        public void Execute(List<IEvolvable<int>> initialPopulation)
        {
            if (initialPopulation == null)
                throw new NullReferenceException("初始种群不能为null");
            else
                Population = initialPopulation;
            while ((this as IGeneticAlgorithm<int>).CanContinue)
            {                               
                Population.AddRange(Reproduce(ScreenParents(Population)));
                Population = ScreenIndividuals(Population);
                CurrentIterationNumber += 1;
            }          
        }
    }
}
